#include "ship.hpp"
#include "room.hpp"
#include "item.hpp"
#include "tile.hpp"
#include "subsys.hpp"


void ship::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
	states.texture = &(*vertTex);
	target.draw(vertMap, states);

	for( size_t i = 0; i < itemList.size(); i++ )
	{
		target.draw(*itemList[i],states);
	}
}

ship::ship(size_t inw, size_t inh)
{
	w = inw;
	h = inh;
	roomID = 0;
	tileMap.resize(w*h);
	pathMap.resize(w*h);
	vertMap.resize(w*h*4);
	vertMap.setPrimitiveType(sf::Quads);
	// sets up tile grid
	for( size_t i = 0; i < w; i++ )
	{
		for( size_t j = 0; j < h; j++ )
		{
			sf::Vertex* quad = &vertMap[ (i+j*w)*4 ];
			quad[0].position = sf::Vector2f(  i    * tileSize,  j    * tileSize);
			quad[1].position = sf::Vector2f( (i+1) * tileSize,  j    * tileSize);
			quad[2].position = sf::Vector2f( (i+1) * tileSize, (j+1) * tileSize);
			quad[3].position = sf::Vector2f(  i    * tileSize, (j+1) * tileSize);
		}
	}
}

bool ship::isVoid(sf::Vector2f loc)
{
	tile* t = getTile(loc);
	if( t )
	{
		return(t->isVoid());
	}
	else
	{
		return(false);
	}
}

void ship::refreshDisp()
{
	for( size_t i = 0; i < w; i++ )
	{
		for( size_t j = 0; j < h; j++ )
		{
			tile * tilePtr = getTile(i,j);
			size_t texID = tilePtr->texID;
			size_t xID;
			size_t yID;

			size_t texWidth = vertTex->getSize().x/tileSize;

			xID = texID % texWidth;
			yID = texID / texWidth;

			sf::Vertex* quad = &vertMap[ (i+j*w)*4 ];
			quad[0].texCoords = sf::Vector2f(  xID    * tileSize,  yID    * tileSize);
			quad[1].texCoords = sf::Vector2f( (xID+1) * tileSize,  yID    * tileSize);
			quad[2].texCoords = sf::Vector2f( (xID+1) * tileSize, (yID+1) * tileSize);
			quad[3].texCoords = sf::Vector2f(  xID    * tileSize, (yID+1) * tileSize);
		}
	}
}

void ship::tick(float timeStep)
{
	for( size_t i = 0; i < sysList.size(); i++ )
	{
		sysList[i]->tick(timeStep);
	}
}

std::string ship::toString()
{
	std::stringstream stream;

	for( size_t j = 0; j < h; j++ )
	{
		for( size_t i = 0; i < w; i++ )
		{
			if( getTile(i,j) )
			{
				stream << getTile(i,j)->toString();
				if( i < w-1 )
					stream << ",";
			}
			else
			{
				std::cout << "tile (" << i << "," << j << ") null\n";
			}

		}
		stream << "\n";
	}

	return(stream.str());
}

void ship::makeRoom(size_t i, size_t j)
{
	if( !getTile(i,j)->isPath() )
	{
		// invalid room, do nothing
	}
	else
	{
		std::shared_ptr<room> troom = std::make_shared<room>();
		floodRoom(i,j, troom);

		for( size_t k = 0; k < roomList.size() && troom->ID != -2; k++ )
		{

			bool overlap = false;
			for(size_t ki = 0; ki < troom->tileList.size() && !overlap; ki++)
			{
				for(size_t kj = 0; kj < roomList[k]->tileList.size() && !overlap; kj++)
				{
					if(troom->tileList[ki] == roomList[k]->tileList[kj])
					{
						overlap = true;
						break;
					}
				}

				if(overlap)
				{
					troom->ID = -2;
				}
			}
		}

		if(troom->ID != -2)
		{
			troom->ID = roomID;
			roomID++;
			roomList.push_back(troom);
		}
	}
}

void ship::floodRoom(size_t i, size_t j, std::shared_ptr<room> inroom)
{
	if(i<w && j<h && i>=0 && j>=0)
	{
		if( getTile(i,j)->isPath() )
		{
			if(i == 0 || j == 0)
			{
				// invalid room; goes outside
				inroom->ID = -2;
			}
			else
			{
				bool check = false;
				for( size_t k = 0; k < inroom->tileList.size(); k++ )
				{
					if( inroom->tileList[k] == getTile(i,j) )
					{
						check = true;
						break;
					}
				}

				if(!check)
				{
					inroom->tileList.push_back(getTile(i,j));
					floodRoom(i+1, j  , inroom);
					floodRoom(i-1, j  , inroom);
					floodRoom(i,   j+1, inroom);
					floodRoom(i,   j-1, inroom);
				}

			}
		}
	}


}


// i = column number, j = row number (i<=>x, j<=>y), 0 based indexing
tile * ship::getTile(size_t i, size_t j)
{
	if(i<w && j<h && i>=0 && j>=0)
	{
//		std::cout << "tile (" << i << "," << j << ") valid\n";
		return( &tileMap[i+j*w] );
	}
	else
	{
		return(nullptr);
	}

}

// location based tile getter, returns null if outside the ship bounds
tile * ship::getTile(sf::Vector2f loc)
{
	if(loc.x < 0 || loc.x > w || loc.y < 0 || loc.x > h )
	{
		return(nullptr);
	}
	else
	{
		int i = floor(loc.x);
		int j = floor(loc.y);
		return(getTile(i,j));
	}
}

void ship::setTile(size_t i, size_t j, tile intile)
{
	if( i < w && j < h )
	{
		tileMap[i+j*w] = intile;
	}
}

void ship::setTex(std::shared_ptr<sf::Texture> intex)
{
	vertTex = intex;
}
